Surface relayer sponsorship signal end-to-end (additive, non-breaking)#1006
Surface relayer sponsorship signal end-to-end (additive, non-breaking)#1006matt416 wants to merge 3 commits into
Conversation
`RpcRelayer.feeOptions` now forwards the server's `sponsored: boolean` to callers, and both `feeOptions` and `feeTokens` mark their swallowed-error returns with `failed: true`. The `Relayer` interface and all bundled implementations (Rpc, Sequence, Local, EIP6963, Pk) are widened to match. Additive change: existing consumers ignoring the new fields are unaffected. Downstream sponsorship classifiers should switch from `!feeOption` inference to `sponsored === true` so a real subsidy is no longer indistinguishable from a swallowed `/FeeOptions` error. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`StandardRelayerOption` gains optional `sponsored` and `failed` fields, populated on both construction branches in `transactions.ts` from the relayer SDK's new `feeOptions` return. `isStandardRelayerOption` / `isERC4337RelayerOption` are re-exported so consumers can narrow before reading the new fields. UI consumers that classified sponsorship by "no fee option attached" should switch to `sponsored === true` to distinguish a real subsidy from a swallowed `/FeeOptions` error. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`DappClient.isSponsored(chainId, transactions)` and `ChainSessionManager.isSponsored(calls)` return true only when the relayer's `/FeeOptions` endpoint explicitly reports sponsorship; any error, network failure, or absence of sponsorship returns false. A true result is always safe to surface as "free gas" in UI. Prefer this over inferring sponsorship from an empty `getFeeOptions` array — a swallowed `/FeeOptions` error produces the same empty shape as a real subsidy. `getFeeOptions` is unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e34cf7d09
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| calls: Payload.Call[], | ||
| data?: Hex.Hex, | ||
| ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> | ||
| ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> |
There was a problem hiding this comment.
Keep sponsored optional for relayer compatibility
This makes the public Relayer contract require every feeOptions implementation to return sponsored, so any downstream custom relayer or typed mock that was valid before (Promise<{ options; quote? }>), even if it does not care about the new signal, is no longer assignable to Relayer. Since all new consumers already treat only sponsored === true as sponsored, making this field optional (and defaulting missing values to false at read sites) would preserve the advertised non-breaking/additive change while still distinguishing explicit sponsorship.
Useful? React with 👍 / 👎.
Summary
Fixes the
/FeeOptionsmisclassification described in our internal bug report: a real subsidy and a swallowed/FeeOptionserror currently produce byte-identical option shapes, so any consumer using!feeOptionas the sponsorship discriminator labels failed quotes as "free gas". This PR makes the two cases distinguishable end-to-end without breaking any existing consumer.@0xsequence/relayer—feeOptionsnow forwards the server'ssponsored: boolean; bothfeeOptionsandfeeTokensmark swallowed errors withfailed: true. TheRelayerinterface and all impls (Rpc, Sequence, Local, EIP6963, Pk) are widened to match.@0xsequence/wallet-wdk—StandardRelayerOptiongains optionalsponsored/failed, populated on both construction branches intransactions.ts;isStandardRelayerOption/isERC4337RelayerOptionare now re-exported.@0xsequence/dapp-client— addsDappClient.isSponsored(chainId, txs)(andChainSessionManager.isSponsored(calls)), which returnstrueonly when the relayer explicitly reports sponsorship.Downstream consumers should switch sponsorship classification from
!feeOptioninference tosponsored === true(orisSponsored()) so a real subsidy is no longer indistinguishable from a swallowed error. All three packages getminorchangesets — additive types, no behavior change for unchanged code paths.Test plan
feeOptionsandfeeTokenserror paths (124/124 vitest passing)sponsored === falseandfailed === undefinedon the LocalRelayer/PkRelayer pathpnpm test:anvil)option.sponsored === truefor a genuinely sponsored tx andoption.failed === truefor a forced/FeeOptions400 (e.g. Polygon Morpho capped vault, Aave insufficient-balance deposit)🤖 Generated with Claude Code